# !/usr/bin/env python3
# -*- encoding: utf-8 -*-
# @FileName    :   UI.py
# @DateTime    :   2022/8/23 | 19:57
# @Author      :   iotus3
# @GitHub      :   https://gitee.com/IoTus3
# @Version     :   1.0.0
# @Contact     :   xianglin.fu3@gmail.com
# @License     :   (C)Copyright 2022-2023, LiuYuStudio-NLPR-CASIA
# @Editor      :   Power By PyCharm
# @Desc        :   None


# 引入系统库
import os
import sys

# 引入qt库 -- pyside2/pyside6
from PySide6.QtWidgets import *
from PySide6.QtGui import *

# 引入三方库
from src.main.conf.conf import baseDIR, statusMAPPING, logPath, RUNNING, STOP, STOPPING
from src.main.utils.Thread import NewTaskThread
from src.main.models.Wares import Wares
from src.main.control.ToExcel import write_wares_to_excel
from src.main.utils.Dialog import SMTPDialog, ProxyDialog, LogDialog
from src.main.utils.Scheduler import SCHEDULER


# 主窗口类
class MainWindow(QWidget):

    def __init__(self):
        # super().__init__()
        QWidget.__init__(self)

        # 程序状态
        self.switch = STOP

        # 定义控件
        self.txtLine = None
        self.mainTable = None
        self.statusLbl = None

        # 窗口的标题
        self.setWindowTitle("基于QT的价格检测平台")

        # 设置窗口的icons
        self.setWindowIcon(QIcon("./assets/images/airplay.svg"))

        # 窗口的尺寸 1228: 宽 450: 高
        self.resize(1228, 450)

        # 窗口的位置 -- 设置到屏幕中心位置
        self.WindowLocationCenter()

        # 主要布局方式
        # QVBoxLayout 是垂直布局 QHBoxLayout是水平布局
        mainLayout = QVBoxLayout()

        # 将头部布局加入主要布局中去
        mainLayout.addLayout(self.InitHeaderLayout())

        # 将标题布局加入主要布局中去
        mainLayout.addLayout(self.InitFormLayout())

        # 将表格区域加入主要区域中去
        mainLayout.addLayout(self.InitTableLayout())

        # 添加底部区域到主要区域
        mainLayout.addLayout(self.InitFooterLayout())

        # 主要区域增加弹簧
        # mainLayout.addStretch()

        # 设置主窗口层布局
        self.setLayout(mainLayout)

        # 显示程序
        self.show()

    # 封装一个窗口居中显示的函数
    def WindowLocationCenter(self):
        # 获取中心坐标
        centerXY = QScreen.availableGeometry(QApplication.primaryScreen()).center()
        # 获取窗口坐标
        windowXY = self.frameGeometry()
        # 移动到中心位置
        windowXY.moveCenter(centerXY)
        self.move(windowXY.topLeft())

    # 头部布局初始化
    def InitHeaderLayout(self):
        # 头部布局区域
        headerLayout = QHBoxLayout()
        # 创建开始按钮
        startBtn = QPushButton("开始监测")
        startBtn.clicked.connect(self.EventStartClicked)
        headerLayout.addWidget(startBtn)
        # 创建结束按钮
        endBtn = QPushButton("结束监测")
        endBtn.clicked.connect(self.EventEndClicked)
        headerLayout.addWidget(endBtn)
        # # 创建导入数据的Excel按钮
        # inputExcelBtn = QPushButton("导入数据")
        # inputExcelBtn.clicked.connect(self.EventInputExcelClicked)
        # headerLayout.addWidget(inputExcelBtn)
        # 创建导出数据的Excel按钮
        toExcelBtn = QPushButton("导出数据")
        toExcelBtn.clicked.connect(self.EventToExcelClicked)
        headerLayout.addWidget(toExcelBtn)
        # 加入弹簧压缩
        headerLayout.addStretch()
        # 返回一个头部布局对象
        return headerLayout

    # 标题布局初始化
    def InitFormLayout(self):
        # 标题布局区域
        formLayout = QHBoxLayout()
        # 创建label框
        formLbl = QLabel("添加监测商品:")
        formLayout.addWidget(formLbl)
        # 创建输入框
        txtLine = QLineEdit()
        # 设置提醒文字
        txtLine.setPlaceholderText("请输入商品ID和价格，例如:B0818JJQQ8=88")
        # 赋值到主窗口对象
        self.txtLine = txtLine
        formLayout.addWidget(txtLine)
        # 创建添加按钮
        addBtn = QPushButton("添加")
        # 创建添加按钮的事件
        addBtn.clicked.connect(self.EventAddClicked)
        formLayout.addWidget(addBtn)
        # 返回一个标题布局对象
        return formLayout

    # 表格布局初始化
    def InitTableLayout(self):
        # 表格布局区域
        tableLayout = QHBoxLayout()
        # 创建表格 初始化为0行8列
        mainTable = QTableWidget(0, 8)
        # 添加到主窗口
        self.mainTable = mainTable
        # 设置字体
        tableFont = QFont()
        # 设置字体家族
        # tableFont.setFamily('微软雅黑')  # 字体
        # 字体大小
        tableFont.setPointSize(10)
        # 是否加粗
        tableFont.setBold(True)
        # mainTable载入字体
        mainTable.setFont(tableFont)
        # 设置表头背景色
        mainTable.horizontalHeader().setStyleSheet(
            "QHeaderView::section{background-color:#F8F8F5;font:10pt;color: black;};")
        # 设置表格选中背景色
        # mainTable.setStyleSheet("selection-background-color: #F8F8F5;")
        # 定义表格列名称
        tableHeaderTitle = [
            {"field": "asin", "text": "ASIN", "width": 120},
            {"field": "title", "text": "标题", "width": 150},
            {"field": "url", "text": "URL", "width": 400},
            {"field": "price", "text": "低价", "width": 100},
            {"field": "success", "text": "成功次数", "width": 100},
            {"field": "error", "text": "503次数", "width": 100},
            {"field": "status", "text": "状态", "width": 100},
            {"field": "frequency", "text": "频率 (N秒/次)", "width": 118},
        ]
        # 使用迭代器生成表格
        for index, info in enumerate(tableHeaderTitle):
            # 创建一个表格项
            item = QTableWidgetItem()
            # 表格项的标题
            item.setText(info["text"])
            # 将表格项设置到mainTable中
            mainTable.setHorizontalHeaderItem(index, item)
            # 设置宽度和位置
            mainTable.setColumnWidth(index, info["width"])
        # 初始化表格数据, 读取数据

        # 开启右键设置
        mainTable.setContextMenuPolicy(Qt.CustomContextMenu)
        mainTable.customContextMenuRequested.connect(self.table_right_menu)

        # 将表格添加入表格区域
        tableLayout.addWidget(mainTable)
        return tableLayout

    def table_right_menu(self, pos):

        # 只有选中一行才支持右键
        selected_item_list = self.mainTable.selectedItems()
        if len(selected_item_list) == 0:
            return

        menu = QMenu()
        item_copy = menu.addAction("复制")
        item_log = menu.addAction("查看日志")
        item_log_clear = menu.addAction("清除日志")

        # 选中哪一个
        action = menu.exec(self.mainTable.mapToGlobal(pos))

        if action == item_copy:
            # 复制当前cell,放到粘贴板里面
            clipboard = QApplication.clipboard()
            clipboard.setText(selected_item_list[0].text())
        elif action == item_log:
            # 获取选中的型号
            row_index = selected_item_list[0].row()
            asin = self.mainTable.item(row_index, 0).text().strip()

            # 查看日志: 在dialog中显示日志信息
            dialog = LogDialog(asin)
            dialog.setWindowModality(Qt.ApplicationModal)
            dialog.exec()
        elif action == item_log_clear:
            # 获取选中的型号
            row_index = selected_item_list[0].row()
            asin = self.mainTable.item(row_index, 0).text().strip()

            # 清除日志
            file_name = "{}.log".format(asin)
            file_path = logPath + file_name
            if os.path.exists(file_path):
                os.remove(file_path)

    # 底部布局初始化
    def InitFooterLayout(self):
        # 底部区域
        footerLayout = QHBoxLayout()
        # 创建未检测按钮
        statusLbl = QLabel("未监测", self)
        self.statusLbl = statusLbl
        footerLayout.addWidget(statusLbl)
        # 增加弹簧
        # footerLayout.addStretch()
        # 创建重新初始化按钮
        reinitBtn = QPushButton("重新初始化")
        reinitBtn.clicked.connect(self.EventReinitClicked)
        footerLayout.addWidget(reinitBtn)
        # 创建重新监测按钮
        recheckBtn = QPushButton("重新监测")
        recheckBtn.clicked.connect(self.EventRecheckClicked)
        footerLayout.addWidget(recheckBtn)
        # 创建次数清零按钮
        resetBtn = QPushButton("次数重置")
        resetBtn.clicked.connect(self.EventResetClicked)
        footerLayout.addWidget(resetBtn)
        # 创建删除监测项按钮
        deleteBtn = QPushButton("删除监测")
        deleteBtn.clicked.connect(self.EventDeleteClicked)
        footerLayout.addWidget(deleteBtn)
        # 创建SMTP报警配置
        smtpBtn = QPushButton("SMTP报警配置")
        smtpBtn.clicked.connect(self.EventSMTPClicked)
        footerLayout.addWidget(smtpBtn)
        # 创建代理IP配置
        proxyBtn = QPushButton("代理IP")
        proxyBtn.clicked.connect(self.EventProxyClicked)
        footerLayout.addWidget(proxyBtn)
        # 返回一个底部对象
        return footerLayout

    # 添加点击按钮事件
    def EventAddClicked(self):
        # 获取输入框中的内容
        text = self.txtLine.text()
        # 去掉空格
        text = text.strip()
        # 如果输入的为空
        if not text:
            QMessageBox.warning(self, "错误", "商品的ASIN输入错误或者为空")
            return
        # 分割出asin和price
        asin, price = text.split("=")
        # 价格转换为浮点型
        price = float(price)
        # 加入到表格中（型号、底价）
        newRowList = [asin, "", "", price, 0, 0, 0, 5]
        # 当前创建的表格有多少行
        currentRowCount = self.mainTable.rowCount()
        # 插入新的行
        self.mainTable.insertRow(currentRowCount)
        # 添加表格数据
        for index, ele in enumerate(newRowList):
            if index == 6:
                ele = statusMAPPING[ele]
            mainTableItem = QTableWidgetItem(str(ele))
            # 定义禁止修改的项
            if index in [0, 3, 4, 5, 6, 7]:
                mainTableItem.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            self.mainTable.setItem(currentRowCount, index, mainTableItem)
        # 发送请求自动获取标题
        # important: 不能在主线程中去做执行爬虫，利用协程来完成，利用协程爬虫爬取数据并更新到窗体上
        # 先传参数，再传对象
        thread = NewTaskThread(currentRowCount, asin, self)
        thread.success.connect(self.init_task_success_callback)
        thread.error.connect(self.init_task_error_callback)
        # 一定要记得开始线程
        thread.start()

        pass

    def init_task_success_callback(self, row_index, asin, title, url, price):
        # 更新窗口显示的数据
        # 更新标题列
        cell_title = QTableWidgetItem(title)
        self.mainTable.setItem(row_index, 1, cell_title)
        # 更新url
        cell_url = QTableWidgetItem(url)
        self.mainTable.setItem(row_index, 2, cell_url)
        # 更新price
        cell_price = QTableWidgetItem(str(price))
        cell_price.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        self.mainTable.setItem(row_index, 3, cell_price)
        # 更新状态
        cell_status = QTableWidgetItem(statusMAPPING[1])
        cell_status.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        self.mainTable.setItem(row_index, 6, cell_status)
        # 输入框清空
        self.txtLine.clear()

    def init_task_error_callback(self, row_index, asin, title, url):
        # 更新窗口显示的数据
        # 更新标题列
        cell_title = QTableWidgetItem(title)
        self.mainTable.setItem(row_index, 1, cell_title)
        # 更新url
        cell_url = QTableWidgetItem(url)
        self.mainTable.setItem(row_index, 2, cell_url)
        # 更新状态
        cell_status = QTableWidgetItem(statusMAPPING[11])
        cell_status.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        self.mainTable.setItem(row_index, 6, cell_status)
        # 输入框清空
        self.txtLine.clear()

    # 点击重新初始化
    def EventReinitClicked(self):
        # 获取选中的行
        row_list = self.mainTable.selectionModel().selectedRows()
        if not row_list:
            QMessageBox.warning(self, "错误", "请选择要重新初始化的行")
            return
        # 获取每一行进行重新初始化
        for row_obj in row_list:
            # 获取index
            index = row_obj.row()

            # 获取asin
            asin = self.mainTable.item(index, 0).text().strip()

            # 更新状态 -- 重新初始化
            cell_status = QTableWidgetItem(statusMAPPING[0])
            cell_status.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            self.mainTable.setItem(index, 6, cell_status)

            # 创建线程去重新初始化
            thread = NewTaskThread(index, asin, self)
            thread.success.connect(self.init_task_success_callback)
            thread.error.connect(self.init_task_error_callback)
            # 一定要记得开始线程
            thread.start()

    # 点击次数清零
    def EventResetClicked(self):
        # 获取选中的行
        row_list = self.mainTable.selectionModel().selectedRows()
        if not row_list:
            QMessageBox.warning(self, "错误", "请选择要次数清零的行")
            return

        # 获取每一行进行次数清零
        for row_obj in row_list:
            # 获取index
            index = row_obj.row()

            # 成功次数清零
            cell_sucess = QTableWidgetItem(str(0))
            cell_sucess.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            self.mainTable.setItem(index, 4, cell_sucess)

            # 失败次数清零
            cell_error = QTableWidgetItem(str(0))
            cell_error.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            self.mainTable.setItem(index, 5, cell_error)

    # 点击删除监测项
    def EventDeleteClicked(self):
        # 获取选中的行
        row_list = self.mainTable.selectionModel().selectedRows()
        if not row_list:
            QMessageBox.warning(self, "错误", "请选择要操作的行")
            return

        # 反转
        row_list.reverse()

        # 获取每一行进行操作
        for row_obj in row_list:
            # 获取index
            index = row_obj.row()

            # 删除监测项
            self.mainTable.removeRow(index)

    # # 点击导入数据
    # def EventInputExcelClicked(self):
    #     pass

    # 点击导出数据
    def EventToExcelClicked(self):
        # 存储每一个wares对象信息的列表
        itemsList = []
        # 获取当前的表格行数
        currentRowCount = self.mainTable.rowCount()
        # 获取每一行的数据
        for row in range(0, currentRowCount):
            # 获取asin
            asin = self.mainTable.item(row, 0).text().strip()

            # 获取title
            title = self.mainTable.item(row, 1).text().strip()

            # 获取url
            url = self.mainTable.item(row, 2).text().strip()

            # 获取价格
            price = self.mainTable.item(row, 3).text().strip()

            # 转化price为float
            price = float(price)

            # 创建Wares对象
            wares = Wares(asin, title, url, price)

            # 添加到对象列表中保存起来
            itemsList.append(wares)

        try:
            write_wares_to_excel(itemsList)
            QMessageBox.warning(self, "成功", "导出数据成功")
        except Exception as e:
            QMessageBox.warning(self, "失败", str(e))

    # 点击邮件配置
    def EventSMTPClicked(self):
        # 创建弹窗并在弹窗中进行设置
        dialog = SMTPDialog()
        dialog.setWindowModality(Qt.ApplicationModal)
        dialog.exec()

    # 点击设置代理
    def EventProxyClicked(self):
        # 创建弹窗并在弹窗中进行设置
        dialog = ProxyDialog()
        dialog.setWindowModality(Qt.ApplicationModal)
        dialog.exec()

    # 开始监测
    def EventStartClicked(self):

        # 判断程序状态
        if self.switch != STOP:
            QMessageBox.warning(self, "错误", "正在监测或者中止，请勿重复操作")

        # 程序状态
        self.switch = RUNNING

        # 为每一行创建一个线程去执行（所有的线程记录）
        SCHEDULER.start(
            self,
            self.task_start_callback,
            self.task_stop_callback,
            self.task_counter_callback,
            self.task_fail_callback,
            self.task_price_callback,
            self.task_email_callback,
        )

        # 执行中
        self.UpdateStatusMessage("执行中")

    # 停止监测
    def EventEndClicked(self):

        if self.switch != RUNNING:
            QMessageBox.warning(self, "错误", "已中止或者正在中止, 请勿重复操作")

        # 程序状态
        self.switch = STOPPING

        # 执行中的线程逐一停止
        SCHEDULER.stop(
            self.UpdateStatusMessage,
        )
        # 更新状态

    # 执行状态更新
    def UpdateStatusMessage(self, message):
        if message == "已中止全部监测项":
            self.switch = STOP

        self.statusLbl.setText(message)
        self.statusLbl.repaint()

    # 开始监测的回调函数
    def task_start_callback(self, row_index):
        # 对表格中的数据进行状态更新
        cell_status = QTableWidgetItem(statusMAPPING[2])
        cell_status.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        self.mainTable.setItem(row_index, 6, cell_status)

    # 执行次数统计
    def task_counter_callback(self, row_index):
        # 原有次数+1
        old_count = self.mainTable.item(row_index, 4).text().strip()
        new_count = int(old_count) + 1

        # 重新表格赋值
        cell_count = QTableWidgetItem(str(new_count))
        cell_count.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        self.mainTable.setItem(row_index, 4, cell_count)

    def task_fail_callback(self, row_index):
        # 原有次数+1
        old_count = self.mainTable.item(row_index, 5).text().strip()
        new_count = int(old_count) + 1

        # 重新表格赋值
        cell_count = QTableWidgetItem(str(new_count))
        cell_count.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        self.mainTable.setItem(row_index, 5, cell_count)

    def task_price_callback(self, row_index, price):
        # 实时更新价格
        cell_price = QTableWidgetItem(str(price))
        cell_price.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        self.mainTable.setItem(row_index, 3, cell_price)

    # 停止
    def task_stop_callback(self, row_index):
        # 对表格中的数据进行状态更新
        cell_status = QTableWidgetItem(statusMAPPING[1])
        cell_status.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        self.mainTable.setItem(row_index, 6, cell_status)

    def EventRecheckClicked(self):
        # 重新监测就是先停止再重新执行
        self.EventEndClicked()
        self.EventStartClicked()

    # 邮箱的回调函数
    def task_email_callback(self, message):
        if message == "成功":
            QMessageBox.warning(self, "成功", "降价邮件发送成功")
        elif message == "失败":
            QMessageBox.warning(self, "失败", "降价邮件发送失败")

